/*
 * @ProjectName: 编程学习
 * @Copyright:   2019 HangZhou Yi Dev, Ltd. All Right Reserved.
 * @address:     https://yiyuery.github.io/
 * @date:        2019/12/18 8:40 上午
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用，转发请注明出处.
 */
package com.example.jvm.gc;

import lombok.AllArgsConstructor;
import sun.misc.Unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 *  一直在做 Full GC
 *  -Xms30m -Xmx50m
 * </p>
 *
 * @author Yi
 * @version V1.0.0
 * @date 2019/12/18 8:40 上午
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人}  2019/12/18
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class MapClear {
    private static BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>(5);
    private static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 4, 300, TimeUnit.MICROSECONDS, workQueue, new MyThreadFactory());
    private static Map<String, Object> cacheMap = new HashMap<>(1024);
    private static int rejectTime = 0;
    private static boolean fullAndWaitEmpty = false;

    public static void main(String[] args) {
        poolExecutor.execute(new Monitor());
        for (int i = 0; i < 200; i++) {
            try {
                    try {
                        if (fullAndWaitEmpty) {
                            Thread.sleep(5*1000);
                        }else{
                            Thread.sleep(1*1000);
                        }

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                poolExecutor.execute(new Task("task-" + i + "-"));
            } catch (RejectedExecutionException e) {
                rejectTime++;
                fullAndWaitEmpty = true;
                System.out.println("RejectedExecutionException happen: rejectTime>" + rejectTime);
            }
        }
    }

    @AllArgsConstructor
    static class Task implements Runnable {

        private String taskName;

        @Override
        public void run() {
            int i = 2*10000;
            while (i-- > 0) {
                if (!fullAndWaitEmpty) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                cacheMap.put(taskName + "-" + i, UUID.randomUUID().toString());
            }
            System.out.println(taskName + ">>>put over");
        }
    }

    private static boolean isFull() {
        return poolExecutor.getActiveCount() == poolExecutor.getMaximumPoolSize() && poolExecutor.getQueue().size() == 5;
    }

    private static boolean isEmpty() {
        return poolExecutor.getActiveCount() == 1 && poolExecutor.getQueue().size() == 0;
    }

    private static boolean notFull() {
        return poolExecutor.getQueue().size() < 5;
    }

    static class Monitor implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    if (isEmpty()) {
                        fullAndWaitEmpty = false;
                    }
                    logThreadInfo();
                    if (cacheMap.size() > 100000) {
                        cacheMap = new HashMap<>();
                        System.out.println("重新指定引用！------");
                    }

                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }


    }

    private static void logThreadInfo() {
        System.out.println(">-----------------------------------------------<");
        System.out.println("getActiveCount " + poolExecutor.getActiveCount());
        System.out.println("getCorePoolSize " + poolExecutor.getCorePoolSize());
        System.out.println("getMaximumPoolSize " + poolExecutor.getMaximumPoolSize());
        System.out.println("getPoolSize " + poolExecutor.getPoolSize());
        System.out.println("getTaskCount " + poolExecutor.getTaskCount());
        System.out.println("getQueueSize " + poolExecutor.getQueue().size());
        System.out.println("fullAndWaitEmpty " + fullAndWaitEmpty);
        System.out.println("cacheMap.size " + cacheMap.size());
        System.out.println("rejectTime " + rejectTime);
    }

    static class MyThreadFactory implements ThreadFactory {

        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public MyThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            namePrefix = "capsule-pool-" +
                    poolNumber.getAndIncrement() +
                    "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }
}
